﻿using System;
using System.IO;
using System.Reflection;
using System.Web;
using System.Web.Security;

namespace dotObjects.Web.Rendering.Handlers
{
    /// <summary>
    /// Class responsible for handling embedded resources.
    /// </summary>
    /// <remarks>
    /// The Framework uses some resources that need to be accessed by the web layer, as JScripts files, 
    /// images,and so on. Those resources are embeded inside the framework's assembly so the user doesn´t need
    /// to copy those to the web application file tree. In order to access those resources then, a URI representation 
    /// is needed. 
    /// <para>This class acts as a web handler that receives a parameter with a string that is generated by the 
    /// <see cref="GetResourceUrl(string)"/> and sends the contents of the
    /// specified resource through the Response Stream.</para>
    /// </remarks>
    internal class ResourceHandler : IHttpHandler
    {
        public const string Path = "dotObjectsWebResource.axd";
        public const string ResourceKey = "r";

        #region IHttpHandler Members

        ///<summary>
        ///Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"></see> interface.
        ///</summary>
        ///
        ///<param name="context">An <see cref="T:System.Web.HttpContext"></see> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests. </param>
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "";
            if (!context.Request.AppRelativeCurrentExecutionFilePath.EndsWith(Path))
                throw new HttpRequestValidationException(
                    string.Format("The path {0} is invalid!", context.Request.AppRelativeCurrentExecutionFilePath));
            RenderResource(context);
        }

        ///<summary>
        ///Gets a instance indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"></see> instance.
        ///</summary>
        ///
        ///<returns>
        ///true if the <see cref="T:System.Web.IHttpHandler"></see> instance is reusable; otherwise, false.
        ///</returns>
        ///
        public bool IsReusable
        {
            get { return false; }
        }

        #endregion

        private static void RenderResource(HttpContext context)
        {
            Stream stream = GetResourceStream(context.Request[ResourceKey]);
            byte[] buffer = new byte[stream.Length];
            stream.Read(buffer, 0, (int) stream.Length);
            context.Response.BinaryWrite(buffer);
        }

        private static Stream GetResourceStream(string resource)
        {
            foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (string resourceName in asm.GetManifestResourceNames())
                {
                    if (resource.Equals(EncodeResource(resourceName)))
                        return asm.GetManifestResourceStream(resourceName);
                }
            }
            return Stream.Null;
        }

        public static string GetResourceUrl(string resource)
        {
            return GetResourceUrl(resource, typeof (ResourceHandler).Assembly);
        }

        public static string GetResourceUrl(string resource, Assembly asm)
        {
            foreach (string resourceName in asm.GetManifestResourceNames())
            {
                if (resource.Equals(resourceName))
                {
                    string appPath = HttpContext.Current.Request.ApplicationPath;
                    if (appPath != null)
                        return appPath + ((appPath.Equals("/")) ? "" : "/") + Path
                               + "?" + ResourceKey + "=" + EncodeResource(resource);
                }
            }
            throw new Exception(string.Format("The resource {0} cannot be found!", resource));
        }

        private static string EncodeResource(string resource)
        {
            return FormsAuthentication.HashPasswordForStoringInConfigFile(resource, "SHA1");
        }
    }
}